//
// Created by admin on 2021/2/15.
//

#include "RenderItemInstanced.h"
#include "AABB.h"
#include "Buffer.h"
#include "EngineCore.h"
#include <random>
#include <unordered_map>
#include <unordered_set>
#include "VertexAttrib.h"
#include "DummyModel.h"

RenderItemInstanced<GraphicsAPI::GLESv3>::RenderItemInstanced(int number, const Phy::AABB & bounding_box, DummyItemType _dummy_type) : IRenderItem(), dummy_type(_dummy_type)
{
    GL_CHECK(glGenVertexArrays(1, &vertexArrayObject));

    instancedNumber = number;

    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_real_distribution<float_t> dist(0.0,1.0);

    instanceMatrixVector.resize(instancedNumber);
    particleList.resize(instancedNumber);

    LOGI("instancedNumber = %d", instancedNumber);
    for (int i = 0; i < instancedNumber; i++)
    {
        float x = dist(rng) * (bounding_box.maxPoint.x - bounding_box.minPoint.x) + bounding_box.minPoint.x;
        float y = dist(rng) * (bounding_box.maxPoint.y - bounding_box.minPoint.y) + bounding_box.minPoint.y;
        float z = dist(rng) * (bounding_box.maxPoint.z - bounding_box.minPoint.z) + bounding_box.minPoint.z;

        instanceMatrixVector[i] = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z));

        LOGI("instance : (%f, %f, %f)", x, y, z);
    }
    InitStructures();
}

RenderItemInstanced<GraphicsAPI::GLESv3>::RenderItemInstanced(int number, const glm::mat4 * matrix_vec,  DummyItemType _dummy_type): IRenderItem(), dummy_type(_dummy_type)
{
    GL_CHECK(glGenVertexArrays(1, &vertexArrayObject));
    instancedNumber = number;
    instanceMatrixVector.resize(instancedNumber);
    particleList.resize(instancedNumber);

    for (int i = 0; i < instancedNumber; i++)
    {
        instanceMatrixVector[i] = matrix_vec[i];
    }

    InitStructures();
}

RenderItemInstanced<GraphicsAPI::GLESv3>::RenderItemInstanced(int x_num, int y_num, int z_num, float leap, glm::vec3 start_point,  DummyItemType _dummy_type): IRenderItem(), dummy_type(_dummy_type)
{
    GL_CHECK(glGenVertexArrays(1, &vertexArrayObject));
    instancedNumber = x_num * y_num * z_num;
    instanceMatrixVector.resize(instancedNumber);
    particleList.resize(instancedNumber);

    for (int i = 0; i < x_num; i++)
        for (int j = 0; j < y_num; j++)
            for (int k = 0; k < z_num; k ++)
            {
                int index = j * x_num * z_num + i * z_num + k;
                float x = i * leap;
                float y = j * leap;
                float z = k * leap;
                instanceMatrixVector[index] = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z) + start_point);
                particleList[index].position = glm::vec4(glm::vec3(x, y, z) + start_point, 1.0f);
                particleList[index].velocity = glm::vec4(glm::sphericalRand(1.0f), 1.0f);
            }
    InitStructures();
}


void RenderItemInstanced<GraphicsAPI::GLESv3>::InitStructures()
{
    auto attrib_type_list = std::unordered_set<VertexAttribType> {
            VertexAttribType::POSITION_F3,
            VertexAttribType::UV1_F2
    };

    switch(dummy_type) {
        case DummyItemType::Plane:
            model = DummyModel::MakeDummyModel<DummyItemType::Plane>(attrib_type_list, false, 10.0f, 10.0f);
            break;

        case DummyItemType::Triangle:
            break;

        case DummyItemType::Box:
            model = DummyModel::MakeDummyModel<DummyItemType::Box>(attrib_type_list, false, 1.0f, 1.0f, 1.0f);
            break;

        case DummyItemType::Sphere:
            break;

        case DummyItemType::Icosahedron:
            model = DummyModel::MakeDummyModel<DummyItemType::Icosahedron>(attrib_type_list, false, 0.05f);
            break;
    }

    LOGI("model ... ");

    AppendVertexAttribData(
            VertexAttribType::POSITION_F3,
            model->GetVertexData(VertexAttribType::POSITION_F3),
            model->GetVertexDataLength(VertexAttribType::POSITION_F3),
            0, 0);

    AppendVertexAttribData(
            VertexAttribType::UV1_F2,
            model->GetVertexData(VertexAttribType::UV1_F2),
            model->GetVertexDataLength(VertexAttribType::UV1_F2),
            0, 0);

    glBindVertexArray(vertexArrayObject);

    instanceMatrixBuffer = std::make_shared<VertexBuffer<GraphicsAPI::GLESv3>>(GL_ARRAY_BUFFER, GL_STATIC_DRAW, 0, sizeof(glm::mat4) * instancedNumber, instanceMatrixVector.data());

    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)0);
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)));
    glEnableVertexAttribArray(4);
    glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)));
    glEnableVertexAttribArray(5);
    glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)));

    glVertexAttribDivisor(2, 1);
    glVertexAttribDivisor(3, 1);
    glVertexAttribDivisor(4, 1);
    glVertexAttribDivisor(5, 1);

    glBindVertexArray(0);

    vertexNumber = model->GetVertexNumber();
    faceNumber = model->GetFaceNumber();
    SetElementData(model->GetIndexData(), model->GetIndexDataLength());
}

void RenderItemInstanced<GraphicsAPI::GLESv3>::AppendVertexAttribData(VertexAttribType attrib_type, const void * data, uint size, uint stride, uint offset)
{
    GL_CHECK(glBindVertexArray(vertexArrayObject));

    /*
     * GLESBuffer 构造函数参数
     * GLenum buffer_target, GLenum usage, long offset, long size, const void * data
     * */

    GL_CHECK(vertexAttribBufferList.emplace_back(
            std::make_pair(
                    attrib_type,
                    std::make_shared<VertexBuffer<GraphicsAPI::GLESv3>>(GL_ARRAY_BUFFER, GL_STATIC_DRAW, offset, size, data)
            )
    ));

    int index = (int) vertexAttribBufferList.size() - 1;

    GL_CHECK(glVertexAttribPointer(
            index,
            GLESVertexAttribToCount[(int) attrib_type],
            GLESVertexAttribToEnum[(int) attrib_type],
            GL_FALSE,
            stride,
            reinterpret_cast<GLvoid*>(offset)
    ));

    GL_CHECK(glEnableVertexAttribArray(index));

    GL_CHECK(glBindVertexArray(0));

    TryGetGLError("GLESRenderItem::AppendVertexAttribData");
}

void RenderItemInstanced<GraphicsAPI::GLESv3>::SetElementData(const void * element_data, size_t element_data_length)
{
    GL_CHECK(glBindVertexArray(vertexArrayObject));
    /*
     * 函数原型
     * GLESBuffer(GLenum buffer_target, GLenum usage, long offset, long size, const void * data);
     * */
    vertexIndexBuffer = GL_CHECK(std::make_shared<VertexBuffer<GraphicsAPI::GLESv3>>(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, 0, element_data_length, (const void *) element_data));
    GL_CHECK(glBindVertexArray(0));
    TryGetGLError("GLESRenderItem::SetElementData");
}